//
// Created by Administrator on 2020/11/08.
//

#include "DNFFmepeg.h"
#include <cstring>
#include "macro.h"


void *task_prepare(void *args) {
    DNFFmepeg *fmepeg = static_cast<DNFFmepeg *>(args);
    fmepeg->_prepare();
    return 0;

}

DNFFmepeg::DNFFmepeg(JavaCallHelper *callHelper, const char *dataSource) {
    this->callHelper = callHelper;
    this->dataSource = new char[strlen(dataSource) + 1];
    strcpy(this->dataSource, dataSource);
}

DNFFmepeg::~DNFFmepeg() {
    delete dataSource;
    dataSource = 0;
}

void DNFFmepeg::prepare() {
    //创建一个线程
    pthread_create(&pid, 0, task_prepare, this);
}

void DNFFmepeg::_prepare() {
    //初始化网络
    avformat_network_init();
    //打开播放地址
    formatContext = 0;
    int ret = avformat_open_input(&formatContext, dataSource, 0, 0);
    //ret不为0表示 打开媒体失败
    if (ret != 0) {
        LOGE("打开媒体失败:%s", av_err2str(ret));
        callHelper->onError(THREAD_CHILD, FFMPEG_CAN_NOT_OPEN_URL);
        return;
    }
    //寻找视频流
    ret = avformat_find_stream_info(formatContext, 0);
    // 小于0 则失败
    if (ret < 0) {
        LOGE("查找流失败:%s", av_err2str(ret));
        callHelper->onError(THREAD_CHILD, FFMPEG_CAN_NOT_FIND_STREAMS);
        return;
    }

    for (int i = 0; i < formatContext->nb_streams; ++i) {
        AVStream *avStream = formatContext->streams[i];

        AVCodecParameters *parameters = avStream->codecpar;

        AVCodec *avCodec = avcodec_find_decoder(parameters->codec_id);
        if (avCodec == NULL) {
            LOGE("查找解码器失败:%s", av_err2str(ret));
            callHelper->onError(THREAD_CHILD, FFMPEG_FIND_DECODER_FAIL);
            return;
        }

        AVCodecContext *avCodecContext = avcodec_alloc_context3(avCodec);
        if (avCodecContext == NULL) {
            LOGE("创建解码上下文失败:%s", av_err2str(ret));
            callHelper->onError(THREAD_CHILD, FFMPEG_ALLOC_CODEC_CONTEXT_FAIL);
            return;
        }
        ret = avcodec_parameters_to_context(avCodecContext, parameters);
        //失败
        if (ret < 0) {
            LOGE("设置解码上下文参数失败:%s", av_err2str(ret));
            callHelper->onError(THREAD_CHILD, FFMPEG_CODEC_CONTEXT_PARAMETERS_FAIL);
            return;
        }
        ret = avcodec_open2(avCodecContext, avCodec, 0);
        if (ret != 0) {
            LOGE("打开解码器失败:%s", av_err2str(ret));
            callHelper->onError(THREAD_CHILD, FFMPEG_OPEN_DECODER_FAIL);
            return;
        }

        AVRational base = avStream->time_base;


        if (parameters->codec_type == AVMEDIA_TYPE_AUDIO) {
          audioChannel = new AudioChannel(i,avCodecContext,base);
        } else if (parameters->codec_type == AVMEDIA_TYPE_VIDEO) {
            AVRational avRational = avStream->avg_frame_rate;
            int fps = av_q2d(avRational);
          videoChannel =  new VideoChannel(i, avCodecContext,base,fps);
          videoChannel->setRenderFrameCallback(callback);
        }

    }
    //没有音视频  (很少见)
    if ( !videoChannel) {
        LOGE("没有音视频");
        callHelper->onError(THREAD_CHILD, FFMPEG_NOMEDIA);
        return;
    }
    // 准备完了 通知java 你随时可以开始播放
    callHelper->onPrepare(THREAD_CHILD);

}

void *play(void *args) {
    DNFFmepeg *ffmpeg = static_cast<DNFFmepeg *>(args);
    ffmpeg->_start();
    return 0;
}

void DNFFmepeg::start() {
    isPlaying=1;
    if (videoChannel){
        //设置为工作状态
        videoChannel->packets.setWork(1);
        videoChannel->setAudioChannel(audioChannel);
        videoChannel->play();
    }
    //启动声音的解码与播放
    if (audioChannel){
        audioChannel->packets.setWork(1);
        audioChannel->play();
    }
    pthread_create(&pid_play, 0, play, this);
}

void DNFFmepeg::_start() {
//1、读取媒体数据包(音视频数据包)
    int ret;
    while (isPlaying) {
        AVPacket *packet = av_packet_alloc();
        ret = av_read_frame(formatContext, packet);
        //=0成功 其他:失败
        if (ret == 0) {
            //stream_index 这一个流的一个序号
            if (audioChannel && packet->stream_index == audioChannel->id) {
                audioChannel->packets.push(packet);
            } else if (videoChannel && packet->stream_index == videoChannel->id) {
                videoChannel->packets.push(packet);
            }
        } else if (ret == AVERROR_EOF) {
            //读取完成 但是可能还没播放完

        } else {
            //
        }

    }
}

void DNFFmepeg::setRenderFrameCallback(RenderFrameCallback callback){
    this->callback = callback;
}
